﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Ymatou.User.Dto.Auth;
using YmtAuth.Domain.Model.YmatouUser;
using YmtAuth.Domain.Repository;
using YmtAuth.Dto;
using YmtSystem.CrossCutting;
using YmtAuth.LogService;
using System.Configuration;
using YmtAuth.AppService.Safe;
using YmtAuth.Domain.DomainService;
using YmtAuth.Domain.Shard;
using ServiceStack;
using YmtAuth.Common.Cache;
using YmtAuth.Common.Extend;
using YmtAuth.Common.Utility;
using YmtAuth.Repository.MSSQL;
using YmtAuth.Domain.Model.Token;
using YmtAuth.Common;
using YmtAuth.AppService.ThirdPartyService;
using Ymatou.PerfMonitorClient;
using YmtAuth.Domain.Model.BlacklistUser;
namespace YmtAuth.AppService
{
    public class TokenAppService : ITokenAppService
    {
        private static LocalCache2<string, bool> UserIpCache = new LocalCache2<string, bool>(); 
        private static readonly IUserTokenRepository TokenRepo = LocalServiceLocator.GetService<IUserTokenRepository>();
        private static readonly ISellerOnlineStatusRepository sellerOnlineStatusRepo = LocalServiceLocator.GetService<ISellerOnlineStatusRepository>();

        private static readonly LocalCache2<string, bool> SellerOnlineStatusTokenCache = new LocalCache2<string, bool>();

        //创建token
        public ResponseData<string> CreateUserToken(UserTokenDto dto)
        {
            var collectionName = UserTokenSpecifications.TokenCollectionName(string.Empty);
            var dbName = UserTokenSpecifications.TokenDatabaseName();

            YmatouLoggingService.Info("[CreateUserToken] dto, {0}", dto.ToString());

            UserToken token = null;

            //TODO: 目前仅支持tokenSource是app, wap的按新的token格式解析（买手暂不接入）
            if (YmtAuth.Domain.Model.Token.TokenSource.IsApplyTokenSource(dto.TokenSource) || ConfigUtility.EnableTokensource)
            {
                token = TokenRepo.FindOne(UserTokenSpecifications.MatchUserToken(dto.UserId, dto.TokenSource), dbName, collectionName);
            }
            else
            {
                token = TokenRepo.FindOne(UserTokenSpecifications.MatchUserToken(dto.UserId), dbName, collectionName);
            }
            
            if (token != null)
            {
                return ResponseData<string>.CreateSuccess(token.TokenId, "ok");
            }
            var watch = Stopwatch.StartNew();
            var uToken = new UserToken(dto.UserId, dto.TokenTimeOut, dto.TokenSource, dto.Context);
            uToken.GenerateToken();
            //同步记录TOKEN 2016.6.6        
            TokenRepo.Add(uToken, dbName, collectionName);
            WriteLogAsync(uToken.TokenId, new SignAuthRequestDto { sClientType = dto.TokenSource, TokenSource = dto.TokenSource }, watch.Elapsed.TotalMilliseconds, dto.UserId, "ok", "newtoken");
            watch.Stop();
            YmatouLoggingService.Info("[CreateUserToken] Context: {0}, TokenSource: {1}, TokenOriginalVal: {2}, TokenId: {3}, dbName: {4}, collectionName: {5}，run {6} ms",                 
                dto.Context,
                dto.TokenSource,
                uToken._originalVal,
                uToken.TokenId,
                dbName,
                collectionName,
                watch.ElapsedMilliseconds);
            return ResponseData<string>.CreateSuccess(uToken.TokenId, "ok");
        }
        
        public  ResponseData<TokenResponseDto> GetUserByToken(SignAuthRequestDto dto)
        {
            //检查参数
            YmtSystemAssert.AssertArgumentNotNull(dto, "无效的SignAuthRequestDto");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.Token, "无效的token");

            //YmatouLoggingService.Debug("[GetUserByToken], {0}", dto.ToString());

            var filterInvalidAppSource = FilterInvalidAppSource(dto.sClientType);
            if (!filterInvalidAppSource.Result)
            {
                YmatouLoggingService.Debug("[GetUserByToken], invalid AppSource: {0}, token: {1}, RId: {2}", dto.sClientType, dto.Token, dto.RequestId);
                return ResponseData<TokenResponseDto>.CreateFail(new TokenResponseDto { }, filterInvalidAppSource.LastErrorMessage);
            }

            AuthResponse authResult = null;

            bool spreadToken = ((Math.Abs(dto.Token.GetHashCode()) % 10) + 1) <= ConfigUtility.JAVASpreadRate;
            //TODO: 20161025 WESLEY, JAVA的token认证仅实现了token校验的逻辑，即使其认证成功仍然要依赖.net的认证（记录日志、更新用户IP区域认证时间、记录买手在线状态）

            if (spreadToken)
            {
                ResponseData<TokenResponseDto> res = null;

                using (var mm = MethodMonitor.New("GetUserByToken_Java_TokenVerify"))
                {
                    res =  UserAuthService.GetUserByToken(dto);
                }

                //如果JAVA的token认证返回异常，使用.net的token认证
                if (!string.Equals("400", res.ErrorCode, StringComparison.InvariantCultureIgnoreCase))
                {
                    if (res.Success)
                    {
                        authResult = new AuthResponse()
                        {
                            UserId = res.Result.UserData.UserId,
                            Code = 200,
                            Message = "ok"
                        };
                    }
                    else
                    {
                        authResult = new AuthResponse()
                        {
                            UserId = 0,
                            Code = res.Result.Code.ConvertTo<int>(-200),
                            Message = res.LastErrorMessage
                        };
                    }
                }
            }

            //WESLEY-20161031:  JAVA的认证服务已实现了"记录日志、更新用户IP区域认证时间、记录买手在线状态"，不再需要.net做后续处理（见http://jira.ymatou.cn:8080/browse/JIRACONN-2962）
            if (authResult == null)
            {
                //检查是否需要查询子表
                var childTokenNames = string.IsNullOrEmpty(ConfigurationManager.AppSettings["FindChildToken"])
                                      || ConfigurationManager.AppSettings["FindChildToken"] == "1" ? new string[] { "app", "matouapp" } : null;

                //构造认证token对象
                AbstractAuthTokenStrategy authToken = AuthTokenFactory.Factory(dto.sClientType);

                //声明计时器
                var watch = Stopwatch.StartNew();

                using (var mm = Ymatou.PerfMonitorClient.MethodMonitor.New("auth_GetUserByToken_dotnet_TokenVerify"))
                {
                    //执行认证token
                    authResult = authToken.ExecuteTokenVerify(dto.Token
                                    , rawToken => Action_Extend.ActionWatch(() => FindUserToken(rawToken, dto.TokenSource, childTokenNames), string.Format("Rid:{0},FindUserToken", dto.RequestId))
                                    , userid => Action_Extend.ActionWatch(() => UserStatesAppService.CheckUserLocked2(userid), string.Format("Rid:{0},CheckUserLocked", dto.RequestId))
                                    , () => Action_Extend.ActionWatch(() => CheckIpBlackList(dto.ClientIp), string.Format("Rid:{0},CheckIpBlackList", dto.RequestId)));
                }
                watch.Stop();
                //写入日志
                Action_Extend.ActionWatch(() => WriteAuthLogAsync(dto, watch.Elapsed.TotalMilliseconds, authResult, "gettoken"), string.Format("Rid:{0},WriteAuthLogAsync", dto.RequestId));

                //返回结果
                // 收集常用IP区域
                //Action_Extend.ActionWatch(() => AddTokenAuthIpArea(dto.ClientIp, authResult.UserId, dto.sClientType, authResult.Code, IPAreaSourceType.GetToken), "AddTokenAuthIpArea");           
                //YmatouLoggingService.Debug("[GetUserByToken] Result, UserId: {0}, token: {1}, RId: {2}, code: {3}, Message: {4},appSource:{5},run:{6:N0} ms", authResult.UserId, dto.Token, dto.RequestId, authResult.Code, authResult.Message, dto.sClientType, watch.ElapsedMilliseconds);
                //TODO 更新用户IP区域认证时间

                using (var mm = Ymatou.PerfMonitorClient.MethodMonitor.New("auth_GetUserByToken_UpdateIPSellerOnline"))
                {
                    if (authResult.Code == 200 && !string.IsNullOrEmpty(dto.ClientIp))
                        UpdateUserIpAreaAuthTimeAsync(authResult.UserId, dto.ClientIp, dto.sClientType);

                    if (authResult.UserId > 0)
                    {
                        Task.Factory.StartNew(() =>
                        {
                            if (YmatouBlacklistUserSpecifications.IsSellerOnlineSource(dto.sClientType))
                            {
                                string key = "{0}_{1}_{2}".Fmt(authResult.UserId, SellerOnlineOpType.AppTokenAuth_OpType, authResult.Code == 200 ? "1" : "0");

                                var needUpdate = SellerOnlineStatusTokenCache.GetOrAdd(key, token => new LocalCache2<string, bool>._CacheItem
                                {
                                    Value = true,
                                    ExpiredTime = TimeSpan.FromMinutes(3)
                                }, false);

                                if (needUpdate)
                                {
                                    sellerOnlineStatusRepo.SaveSellerAppOnlineStatus(new SellerOnlineStatusOp()
                                    {
                                        AppSource = dto.sClientType,
                                        UserId = authResult.UserId,
                                        OpType = SellerOnlineOpType.AppTokenAuth_OpType,
                                        Op = authResult.Code == 200 ? "1" : "0",// 1表示成功, 0表示失败
                                        CreateTime = DateTime.Now,
                                        OpTime = DateTime.Now,
                                        LastUpdateTime = DateTime.Now,
                                        OpDescript = "TokenAuth"
                                    });

                                    SellerOnlineStatusTokenCache.Replace(key, false, TimeSpan.FromMinutes(3));
                                }
                            }

                        }).ContinueWith(t => { YmatouLoggingService.Error("GetUserByToken - SaveSellerAppOnlineStatus error", t.Exception); }, TaskContinuationOptions.OnlyOnFaulted);
                    }
                }
            }

            return TokenAuthResponse(authResult.Code, authResult.UserId, authResult.Message);
        }

        public ResponseData<SignAuthResponseDto2> CheckSign(SignAuthRequestDto dto) 
        {
            //检查请求参数
            YmtSystemAssert.AssertArgumentNotNull(dto, "无效的SignAuthRequestDto");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.Sign, "无效的Sign");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.OriginalVal, "无效的OriginalVal");

            YmatouLoggingService.Debug("[CheckSign], {0}", dto.ToString());

            var filterInvalidAppSource = FilterInvalidAppSource(dto.sClientType);
            if (!filterInvalidAppSource.Result)
            {
                YmatouLoggingService.Debug("[CheckSign], invalid AppSource: {0}, RId: {1}", dto.sClientType, dto.RequestId);
                return ResponseData<SignAuthResponseDto2>.CreateFail(new SignAuthResponseDto2 { }, filterInvalidAppSource.LastErrorMessage);
            }

            //检查是否需要查询子表  
            var childTokenNames = string.IsNullOrEmpty(ConfigurationManager.AppSettings["FindChildToken"])
                                 || ConfigurationManager.AppSettings["FindChildToken"] == "1" ? new string[] { "app", "matouapp" } : null;
            //构造认证token策略对象
            AbstractAuthTokenStrategy authToken = AuthTokenFactory.Factory(dto.sClientType);
            //启动计时器
            var watch = Stopwatch.StartNew();
            //执行认证token
            var authResult = Action_Extend.ActionWatch(() => authToken.ExecuteTokenSignVerify(dto.OriginalVal, dto.Sign
                            , rawToken => Action_Extend.ActionWatch(() => FindUserToken(rawToken, dto.TokenSource, childTokenNames), string.Format("RId:{0},FindUserToken", dto.RequestId))
                            , userid => Action_Extend.ActionWatch(() => UserStatesAppService.CheckUserLocked2(userid), string.Format("RId:{0},CheckUserLocked", dto.RequestId))
                            , () => Action_Extend.ActionWatch(() => CheckIpBlackList(dto.ClientIp), dto.RequestId + ", CheckIpBlackList"), dto.RequestId), string.Format("RId:{0},ExecuteTokenSignVerify",dto.RequestId ));              
            // 收集常用IP区域
            //Action_Extend.ActionWatch(() => AddTokenAuthIpArea(dto.ClientIp, authResult.UserId, dto.sClientType, authResult.Code, IPAreaSourceType.AuthToken), dto.RequestId + ", 收集常用IP区域 AddTokenAuthIpArea;");
            watch.Stop();
            //写入日志
            Action_Extend.ActionWatch(() => WriteAuthLogAsync(dto, watch.Elapsed.TotalMilliseconds, authResult, "CheckSign"), string.Format("RId:{0},WriteAuthLogAsync",dto.RequestId));
            
//            YmatouLoggingService.Debug("[CheckSign] Result, UserId: {0}, Sign: {1}, RId: {2}, code: {3}, Message: {4},appSource:{5},run:{6:N0} ms"
//                , authResult.UserId, dto.Sign, dto.RequestId, authResult.Code, authResult.Message, dto.sClientType, watch.ElapsedMilliseconds);
            //TODO 更新用户IP区域认证时间
            //
            if (authResult.Code == 200 && !string.IsNullOrEmpty(dto.ClientIp))
                UpdateUserIpAreaAuthTimeAsync(authResult.UserId, dto.ClientIp, dto.sClientType);

            if (authResult.UserId > 0)
            {
                Task.Factory.StartNew(() =>
                {
                    if (YmatouBlacklistUserSpecifications.IsSellerOnlineSource(dto.sClientType))
                    {
                        string key = "{0}_{1}_{2}".Fmt(authResult.UserId, SellerOnlineOpType.AppTokenAuth_OpType, authResult.Code == 200 ? "1" : "0");

                        var needUpdate = SellerOnlineStatusTokenCache.GetOrAdd(key, token => new LocalCache2<string, bool>._CacheItem
                        {
                            Value = true,
                            ExpiredTime = TimeSpan.FromMinutes(3)
                        }, false);

                        if (needUpdate)
                        {
                            sellerOnlineStatusRepo.SaveSellerAppOnlineStatus(new SellerOnlineStatusOp()
                            {
                                AppSource = dto.sClientType,
                                UserId = authResult.UserId,
                                OpType = SellerOnlineOpType.AppTokenAuth_OpType,
                                Op = authResult.Code == 200 ? "1" : "0",// 1表示成功, 0表示失败
                                CreateTime = DateTime.Now,
                                OpTime = DateTime.Now,
                                LastUpdateTime = DateTime.Now,
                                OpDescript = "TokenAuth"
                            });

                            SellerOnlineStatusTokenCache.Replace(key, false, TimeSpan.FromMinutes(3));
                        }
                    }

                }).ContinueWith(t => { YmatouLoggingService.Error("CheckSign - SaveSellerAppOnlineStatus error", t.Exception); }, TaskContinuationOptions.OnlyOnFaulted);
            }

            return SignAuthResponse(authResult.Code, authResult.UserId, authResult.Message);
        }

        /// <summary>
        /// 过滤无效的应用来源
        /// </summary>
        /// <param name="appSource"></param>
        /// <returns></returns>
        public ResponseData<bool> FilterInvalidAppSource(string appSource)
        {
            ResponseData<bool> res = ResponseData<bool>.CreateSuccess(true);

            int invalidAppSourceCheck = ConfigurationManager.AppSettings["InvalidAppSourceCheck"].ConvertToInt32(0);

            if (invalidAppSourceCheck != 0)
            {
                string invalidAppSource = ConfigurationManager.AppSettings["InvalidAppSourceList"];
                if (!string.IsNullOrEmpty(invalidAppSource))
                {
                    var invalidAppSourceList = invalidAppSource.Split(new char[] { ',' });
                    if (invalidAppSourceList != null && invalidAppSourceList.Any(r => string.Equals(appSource, r, StringComparison.InvariantCultureIgnoreCase)))
                    {
                        res = ResponseData<bool>.CreateSuccess(false, "无效的应用来源:" + appSource);
                    }
                }
            }

            if (string.IsNullOrEmpty(appSource) || !res.Result)
            {
                using (var mm = Ymatou.PerfMonitorClient.MethodMonitor.New("auth_InvalidAppSource"))
                {

                }
            }

            return res;
        }

        //删除用户token   trigger: 1. 业务站点通过http调用  2. 用户更改登录密码
        public ResponseData<string> RemoveToken(UserRemoveTokenRequestDto dto)
        {
            YmtSystemAssert.AssertArgumentNotNull(dto, "无效的UserTokenDto");

            int userId = 0;
            if (dto.UserId > 0)
            {
                userId = dto.UserId;
            }
            else
            {
                if (!string.IsNullOrEmpty(dto.Token))
                {
                    var userToken = new UserToken();
                    userToken.DecryptToken(dto.Token);
                    userId = userToken.UserId;
                }
            }

            if (userId > 0)
            {
                YmatouLoggingService.Debug("删除token, RemoveToken, userId：{0}, 请求参数:{1}", userId, dto.ToJson());
                
                //1. 从mongoDB删除数据
                TokenRepo.RemoveUserToken(userId);

                //2. 删除缓存数据
                TokenCache<string, int>.RemoveItemByValue(userId);

                return ResponseData<string>.CreateSuccess("ok");
            }
            return ResponseData<string>.CreateFail("Error");
        }

        //更新用户IP区域
        private void UpdateUserIpAreaAuthTimeAsync(int userId,string ip,string source)
        {
            if (!CommonHelper.IsValidIPv4(ip))
                return;
            var isUpdate = UserIpCache.GetOrAdd("{0}_{1}".Fmt(userId, ip), uid => new LocalCache2<string, bool>._CacheItem
            {
                Value = true,
                ExpiredTime = TimeSpan.FromDays(1)
            },false);
            if(!isUpdate)
                return;
            if (isUpdate)
            {
                Task.Factory.StartNew(() =>
                {
                    if (isUpdate)
                    {
                        var ipArea = UserSafeVerify.FindUserIpArea(ip);
                        if (ipArea != null)
                        {
                            LogTask.Instance.EnQueue(new UpdateUserIpAreaTimeLog
                            {
                                Ip = ip,
                                UserId = userId,
                                Country = ipArea.Country,
                                Province = ipArea.Province,
                                City = ipArea.City
                            });
                            UserIpCache.Replace("{0}_{1}".Fmt(userId, ip), false, TimeSpan.FromDays(1));
                            YmatouLoggingService.Debug("UpdateUserIpAreaAuthTimeAsync, userId: {0}, ip: {1}, Country: {2}, Province: {3}, City: {4}", userId, ip, ipArea.Country, ipArea.Province, ipArea.City);
                        }
                    }
                }).ContinueWith(t => { /*YmatouLoggingService.Error("UpdateUserIpAreaAuthTimeAsync error", t.Exception);*/ }, TaskContinuationOptions.OnlyOnFaulted);
            }
        }

        private UserToken FindUserToken(string rawToken
            , params string[] childTokenTableNames)
        {
            //读取token 从->主
            var token = TokenRepo.FindOne(UserTokenSpecifications.MatchToken(rawToken)
                , UserTokenSpecifications.TokenDatabaseName()
                , UserTokenSpecifications.TokenCollectionName(string.Empty), false);
            if (token != null) return token;
            //读取token 主->从,如果开启，默认开启
            var enableReadPrimary = ConfigurationManager.AppSettings["ReadToken_PrimaryPreferred"];
            if (string.IsNullOrEmpty(enableReadPrimary) || enableReadPrimary == "1")
            {
                token = TokenRepo.FindOneByPrimaryPreferred(UserTokenSpecifications.MatchToken(rawToken)
                    , UserTokenSpecifications.TokenDatabaseName()
                    , UserTokenSpecifications.TokenCollectionName(string.Empty), false);
                if (token != null)
                {
                    YmatouLoggingService.Debug("FindOneByPrimaryPreferred get UserToken {0}", rawToken);
                    return token;
                }
            }
            //读取子表
            if (childTokenTableNames == null || childTokenTableNames.Length == 0) return null;
            foreach (var name in childTokenTableNames)
            {
                token = TokenRepo.FindOne(UserTokenSpecifications.MatchToken(rawToken)
                , UserTokenSpecifications.TokenDatabaseName()
                , UserTokenSpecifications.TokenCollectionName(name), false);
                if (token != null)
                    return token;
            }
            return null;
        }

        /// <summary>
        /// token查询；先查token，发现tokensource没有，就不匹配传入的tokensource
        /// </summary>
        /// <param name="rawToken"></param>
        /// <param name="tokenSource"></param>
        /// <param name="childTokenTableNames"></param>
        /// <returns></returns>
        private UserToken FindUserToken(string rawToken, string tokenSource
            , params string[] childTokenTableNames)
        {
            //TODO: 目前仅支持tokenSource是app, wap的按新的token格式解析（买手暂不接入）
            if (TokenSource.IsApplyTokenSource(tokenSource) || ConfigUtility.EnableTokensource)
            {
                YmatouLoggingService.Info("FindUserToken, tokenSource: {0}, rawToken: {1}", tokenSource, rawToken);
                
                //读取token 从->主
                var token = TokenRepo.FindOne(UserTokenSpecifications.MatchToken(rawToken)
                    , UserTokenSpecifications.TokenDatabaseName()
                    , UserTokenSpecifications.TokenCollectionName(string.Empty), false);

                if (token != null)
                {
                    if (string.IsNullOrEmpty(token.TokenSource) || string.Equals(tokenSource, token.TokenSource, StringComparison.InvariantCultureIgnoreCase))
                        return token;
                    else
                        return null;
                }

                //读取token 主->从,如果开启，默认开启
                var enableReadPrimary = ConfigurationManager.AppSettings["ReadToken_PrimaryPreferred"];
                if (string.IsNullOrEmpty(enableReadPrimary) || enableReadPrimary == "1")
                {
                    token = TokenRepo.FindOneByPrimaryPreferred(UserTokenSpecifications.MatchToken(rawToken)
                        , UserTokenSpecifications.TokenDatabaseName()
                        , UserTokenSpecifications.TokenCollectionName(string.Empty), false);
                    if (token != null)
                    {
                        if (string.IsNullOrEmpty(token.TokenSource) || string.Equals(tokenSource, token.TokenSource, StringComparison.InvariantCultureIgnoreCase))
                        {
                            YmatouLoggingService.Debug("FindOneByPrimaryPreferred get UserToken {0}", rawToken);
                            return token;
                        }
                        else
                            return null;
                    }
                }
                //读取子表
                if (childTokenTableNames == null || childTokenTableNames.Length == 0) return null;
                foreach (var name in childTokenTableNames)
                {
                    token = TokenRepo.FindOne(UserTokenSpecifications.MatchToken(rawToken)
                    , UserTokenSpecifications.TokenDatabaseName()
                    , UserTokenSpecifications.TokenCollectionName(name), false);
                    if (token != null)
                    {
                        if (string.IsNullOrEmpty(token.TokenSource) || string.Equals(tokenSource, token.TokenSource, StringComparison.InvariantCultureIgnoreCase))
                            return token;
                        else
                            return null;
                    }
                }
                return null;
            }
            else
            {
                YmatouLoggingService.Info("FindUserToken, rawToken: {0}", rawToken);
                
                //读取token 从->主
                var token = TokenRepo.FindOne(UserTokenSpecifications.MatchToken(rawToken)
                    , UserTokenSpecifications.TokenDatabaseName()
                    , UserTokenSpecifications.TokenCollectionName(string.Empty), false);
                if (token != null) return token;
                //读取token 主->从,如果开启，默认开启
                var enableReadPrimary = ConfigurationManager.AppSettings["ReadToken_PrimaryPreferred"];
                if (string.IsNullOrEmpty(enableReadPrimary) || enableReadPrimary == "1")
                {
                    token = TokenRepo.FindOneByPrimaryPreferred(UserTokenSpecifications.MatchToken(rawToken)
                        , UserTokenSpecifications.TokenDatabaseName()
                        , UserTokenSpecifications.TokenCollectionName(string.Empty), false);
                    if (token != null)
                    {
                        YmatouLoggingService.Debug("FindOneByPrimaryPreferred get UserToken {0}", rawToken);
                        return token;
                    }
                }
                //读取子表
                if (childTokenTableNames == null || childTokenTableNames.Length == 0) return null;
                foreach (var name in childTokenTableNames)
                {
                    token = TokenRepo.FindOne(UserTokenSpecifications.MatchToken(rawToken)
                    , UserTokenSpecifications.TokenDatabaseName()
                    , UserTokenSpecifications.TokenCollectionName(name), false);
                    if (token != null)
                        return token;
                }
                return null;
            }
        }

        private void WriteAuthLogAsync(SignAuthRequestDto dto
            , double totalMilliseconds
            , AuthResponse response
            , string descript = null)
        {
            //IP 黑名单请求日志表
            if (response.Code == -80)
                LogTask.Instance.EnQueue(new UserLockLog { OpType = UserLockLog.BlackListIp, LoginTime = DateTime.Now, LoginIp = dto.ClientIp, AppSource = dto.sClientType, LoginId = dto.Token, Description = "blacklistiplocked" });
            //认证token日志表
            else
                WriteLogAsync(dto.Token, dto, totalMilliseconds, response.UserId, response.Code.ToString()
                    , string.Format("{0},{1}", descript, response.Message));
        }       
        private void WriteLogAsync(string token
            , SignAuthRequestDto dto
            , double runTime
            , int userId
            , string summary
            , string description)
        {
            //WESLEY: 20161028, 关闭token日志
            
            //LogTask.Instance.EnQueue(new TokenAuthLog
            //{
            //    iAction = userId,
            //    sLoginId = token,
            //    sLoginIp = dto.ClientIp ?? null,
            //    Desc = description,
            //    sClientType = dto.sClientType,
            //    TSource = dto.TokenSource,
            //    RunTime = runTime,
            //    sSummary = summary,
            //    DeviceId = dto.DeviceId
            //});
        }
        private static bool CheckIpBlackList(string clientIp)
        {
            //判断是否开启token认证检查Ip黑名单 & 执行检查黑单
            return SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration()
                   .TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("TokenAuthCheckIp"),"1") == "1"
                   && BlackListAppService.TryCheckIpIsDisableRequest(clientIp);
        }
        private ResponseData<TokenResponseDto> TokenErrorResult(
            LoginResponseCode code
            , string errMsg = null)
        {
            return ResponseData<TokenResponseDto>
                .CreateFail(new TokenResponseDto { Code = code }
                , lastErrorMessage: errMsg);
        }
        private ResponseData<TokenResponseDto> TokenAuthResponse(int code
            , int userid
            , string message = null)
        {
            if (code == 200 || code == 210) 
                return ResponseData<TokenResponseDto>.CreateSuccess(new TokenResponseDto { Code = LoginResponseCode.OK, UserData = new UserTokenData { UserId = userid } }, message);          
           return ResponseData<TokenResponseDto>.CreateFail(new TokenResponseDto { Code = code == -200 ? LoginResponseCode.UserLocked : LoginResponseCode.TokenInvalid }, code.ToString(), message);          
        }
        private ResponseData<SignAuthResponseDto2> SignAuthResponse(int code
            , int userid
            , string message = null)
        {
            if (code == 200 || code == 210)
                return ResponseData<SignAuthResponseDto2>.CreateSuccess(new SignAuthResponseDto2 { Code = LoginResponseCode.OK }, message);
            return ResponseData<SignAuthResponseDto2>.Create(new SignAuthResponseDto2 { Code = LoginResponseCode.SignError }, true, message, code.ToString());           
        }
        #region [老的业务逻辑，已重构为非 Obsolete 的方法]
        /// <summary>
        /// 创建用户token
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [Obsolete("use CreateUserToken")]
        public ResponseData<string> CreateUserToken_Obsolete(UserTokenDto dto)
        {
            //记录日志
            YmatouLoggingService.Debug("CreateUserToken userid {0} ，source {1}", dto.UserId, dto.TokenSource);
            var watch = Stopwatch.StartNew();
            //查询用户
            //var ulc = UlcRepo.Value.FindOne(UserLoginAuthSpecifications.MatchUserId(dto.UserId), true);
            //if (ulc == null)
            //    return ResponseData<string>.CreateFail(string.Empty, lastErrorMessage: "用户不存在");
            ////判断是否被锁定
            //if (ulc.IsLocked())
            //    return ResponseData<string>.CreateFail(string.Empty, lastErrorMessage: "用户被锁定");
            var tokenTable = UserTokenSpecifications.TokenCollectionName(dto.TokenSource);
            var token = TokenRepo.FindOne(UserTokenSpecifications.MatchUserToken(dto.UserId), UserTokenSpecifications.TokenDatabaseName(), tokenTable);
            //存在token，则直接返回
            if (token != null)
            {
                if (tokenTable != UserTokenSpecifications.TokenMainTable() && TokenRepo.Exists(UserTokenSpecifications.MatchUserToken(dto.UserId)))
                {
                    var _uToken = new UserToken(dto.UserId, dto.TokenTimeOut, dto.TokenSource, dto.Context);
                    _uToken.GenerateToken();
                    TokenRepo.Add(_uToken);
                }
                return ResponseData<string>.CreateSuccess(token.TokenId, "ok");
            }
            //创建新的token 
            var uToken = new UserToken(dto.UserId, dto.TokenTimeOut, dto.TokenSource, dto.Context);
            uToken.GenerateToken();
            //创建并保持新的token
            SaveNewToken(dto, uToken);
            watch.Stop();

            YmatouLoggingService.Debug("创建token，耗时 {0} 毫秒", watch.ElapsedMilliseconds);
            WriteLogAsync(uToken.TokenId, new SignAuthRequestDto { sClientType = dto.TokenSource, TokenSource = dto.TokenSource }, watch.Elapsed.TotalMilliseconds, dto.UserId, "ok", "newtoken");
            //return 
            return ResponseData<string>.CreateSuccess(uToken.TokenId, "ok");
        }
        /// <summary>
        /// 根据token获取用户Id
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        [Obsolete]
        public ResponseData<TokenResponseDto> GetUserByToken_Obsolete(SignAuthRequestDto dto)
        {
            YmtSystemAssert.AssertArgumentNotNull(dto, "无效的SignAuthRequestDto");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.Token, "无效的token");

            //执行时间监控
            var watch = Stopwatch.StartNew();
            var userToken = new UserToken();
            userToken.DecryptToken(dto.Token);
            //如果解密后的原始token 为空，则退出
            if (string.IsNullOrEmpty(userToken.RawToken))
            {
                WriteLogAsync(dto.Token, dto, watch.Elapsed.TotalMilliseconds, userToken.UserId, "formaterror", "gettoken");
                return TokenErrorResult(LoginResponseCode.TokenInvalid, "token无效");
            }
            var tokenCache = GetUserTokenByCache(dto.Token);
            //如果缓存中的token 不是无效的,且userid匹配，则返回
            if (tokenCache.Result.Code != LoginResponseCode.TokenInvalid && userToken.VerifyTokenUserIsMatch(tokenCache.Result.UserData.UserId))
                return tokenCache;

            //查询用户Token
            var _token = TokenRepo.FindOne(UserTokenSpecifications.MatchToken(userToken.RawToken), UserTokenSpecifications.TokenDatabaseName(), UserTokenSpecifications.TokenCollectionName(dto.sClientType), false);
            if (_token == null)
            {
                //主表
                _token = TokenRepo.FindOne(UserTokenSpecifications.MatchToken(userToken.RawToken), UserTokenSpecifications.TokenDatabaseName(), UserTokenSpecifications.TokenCollectionName(null), false);
                if (_token == null)
                {
                    WriteLogAsync(dto.Token, dto, watch.Elapsed.TotalMilliseconds, userToken.UserId, "formaterror", "gettoken");
                    return TokenErrorResult(LoginResponseCode.TokenInvalid, "token不存在");
                }
                _token.ReSetTokenSource(null);
            }
            //验证token
            //如果token userID 和请求验证userId 不匹配则退出
            var verifyResult = _token.VerifyTokenUserIsMatch(userToken.UserId);
            if (!verifyResult)
            {
                WriteLogAsync(dto.Token, dto, watch.Elapsed.TotalMilliseconds, userToken.UserId, "useridnotmatch", "gettoken");
                return TokenErrorResult(LoginResponseCode.TokenInvalid, "token无效");
            }
            //检查用户是否被锁定
            if (UserStatesAppService.EnableGetTokenCheckUserLocked())
            {
                var checkUserResult = UserStatesAppService.CheckUserLocked(_token.UserId);//CheckUserState2(_token.UserId);
                if (checkUserResult != Domain.Shard.UserStates.Normal)
                {
                    WriteLogAsync(dto.Token, dto, watch.Elapsed.TotalMilliseconds, userToken.UserId, "userlocked", "gettoken");
                    return ResponseData<TokenResponseDto>.CreateFail(new TokenResponseDto { Code = LoginResponseCode.UserLocked }, lastErrorMessage: "用户被锁定"); ;
                }
            }
            //是否需要延长token过期时间
            if (_token.IsExtendTokenExpiredTime(_token.TokenSource))
            {
                TokenRepo.Save(_token, UserTokenSpecifications.TokenDatabaseName(), UserTokenSpecifications.TokenCollectionName(_token.TokenSource));
                //TokenRepo.Update(UserTokenSpecifications.MatchUserTokenMongoId(_token.Id), UserTokenSpecifications.ExtendTokenExpiredTimeUpdate(_token.ExpiredTime.Value), UserTokenSpecifications.TokenDatabaseName(), UserTokenSpecifications.TokenCollectionName(_token.TokenSource));
            }
            //记录日志
            watch.Stop();
            WriteLogAsync(_token.TokenId, dto, watch.Elapsed.TotalMilliseconds, _token.UserId, "ok", "gettoken");
            //添加到缓存
            TokenCache<string, int>.TryAdd(dto.Token, _token.UserId);
            //返回
            return ResponseData<TokenResponseDto>.CreateSuccess(new TokenResponseDto { Code = LoginResponseCode.OK, UserData = new UserTokenData { UserId = _token.UserId } }, "ok");
        }
        /// <summary>
        /// 签名校验
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        ///客户端第一次：请求 GetUserIdByToken 成功后，组合 userID+token+... 加密后，再请求 CheckSign 校验，校验成功后，则完成登录 
        [Obsolete]
        public ResponseData<SignAuthResponseDto2> CheckSign_Obsolete(SignAuthRequestDto dto)
        {
            //开关关闭返回 ，签名认证成功（测试用）
            if (UserToken.EnableCheckSign())
            {
                return ResponseData<SignAuthResponseDto2>.CreateSuccess(new SignAuthResponseDto2 { Code = LoginResponseCode.OK }, "ok");
            }

            YmtSystemAssert.AssertArgumentNotNull(dto, "无效的SignAuthRequestDto");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.Sign, "无效的Sign");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.OriginalVal, "无效的OriginalVal");

            //执行时间监控
            var watch = Stopwatch.StartNew();
            //声明token 实例
            var tokenInfo = new UserToken(dto.OriginalVal, dto.Sign);
            //签名验证，如果验证不通过，则返回
            var result = tokenInfo.VerifySign();
            if (result.Item1 != LoginResponseCode.OK)
            {
                watch.Stop();
                WriteLogAsync(tokenInfo.TokenId, dto, watch.Elapsed.TotalMilliseconds, tokenInfo.UserId, "fail", "signfail");

                YmatouLoggingService.Debug("check sign result,OriginalVal :{0} ， Token:{1} ， Sign:{2}，verify {3}", dto.OriginalVal, dto.Token, dto.Sign, result.Item1);
                return ResponseData<SignAuthResponseDto2>.CreateFail(new SignAuthResponseDto2 { Code = result.Item1 }, lastErrorMessage: result.Item2);
            }
            //如果缓存中的token 不是无效的，则返回
            int cacueUserId = 0;
            var cacheToken = GetUserTokenByCache(dto.OriginalVal, dto.Token, out cacueUserId);
            if (cacheToken.Result.Code != LoginResponseCode.TokenInvalid && tokenInfo.VerifyTokenUserIsMatch(cacueUserId))
                return cacheToken;
            //根据token查找用户 
            var token = TokenRepo.FindOne(UserTokenSpecifications.MatchToken(tokenInfo.RawToken), UserTokenSpecifications.TokenDatabaseName(), UserTokenSpecifications.TokenCollectionName(dto.sClientType), false);
            if (token == null)
            {
                token = TokenRepo.FindOne(UserTokenSpecifications.MatchToken(tokenInfo.RawToken), UserTokenSpecifications.TokenDatabaseName(), UserTokenSpecifications.TokenCollectionName(null), false);
                if (token == null)
                {
                    watch.Stop();
                    WriteLogAsync(dto.Token, dto, watch.Elapsed.TotalMilliseconds, tokenInfo.UserId, "notfind", "dbtokennull");
                    return ResponseData<SignAuthResponseDto2>.CreateFail(new SignAuthResponseDto2 { Code = LoginResponseCode.SignError }, lastErrorMessage: "签名错误");
                }
                //设置token客户端来源标识
                tokenInfo.ReSetTokenSource(null);
            }
            //验证token 加密数据userID 是否匹配，不匹配，则返回
            var verifResult = token.VerifyTokenUserIsMatch(tokenInfo.UserId);
            if (!verifResult)
            {
                watch.Stop();
                WriteLogAsync(token.TokenId, dto, watch.Elapsed.TotalMilliseconds, token.UserId, "fail", "verifytokenfail");
                return ResponseData<SignAuthResponseDto2>.CreateFail(new SignAuthResponseDto2 { Code = LoginResponseCode.TokenInvalid }, lastErrorMessage: "token 无效！");
            }
            //检查用户是否被锁定
            if (UserStatesAppService.EnableSignTokenCheckUserLocked())
            {
                var checkUserResult = UserStatesAppService.CheckUserLocked(token.UserId);
                if (checkUserResult != Domain.Shard.UserStates.Normal)
                {
                    WriteLogAsync(tokenInfo.TokenId, dto, watch.Elapsed.TotalMilliseconds, tokenInfo.UserId, "userlocked", "signfail");
                    return ResponseData<SignAuthResponseDto2>.CreateFail(new SignAuthResponseDto2 { Code = LoginResponseCode.UserLocked }, lastErrorMessage: "用户被锁定");
                }
            }
            //检查是否需要延长token
            if (token.IsExtendTokenExpiredTime(token.TokenSource))
            {
                TokenRepo.Save(token, UserTokenSpecifications.TokenDatabaseName(), UserTokenSpecifications.TokenCollectionName(token.TokenSource));
                //TokenRepo.Update(UserTokenSpecifications.MatchUserTokenMongoId(token.Id), UserTokenSpecifications.ExtendTokenExpiredTimeUpdate(token.ExpiredTime.Value), UserTokenSpecifications.TokenDatabaseName(), UserTokenSpecifications.TokenCollectionName(tokenInfo.TokenSource));
            }
            //add log
            watch.Stop();
            WriteLogAsync(token.TokenId, dto, watch.Elapsed.TotalMilliseconds, token.UserId, "ok", "verifytokenok");
            //添加到缓存
            TokenCache<string, int>.TryAdd(dto.OriginalVal, tokenInfo.UserId);
            //返回
            return ResponseData<SignAuthResponseDto2>.CreateSuccess(new SignAuthResponseDto2 { Code = LoginResponseCode.OK }, "ok");
        }
        [Obsolete("use CreateUserToken")]
        public UserToken SaveUserAuthToken_Obsolete(int userId, long tokenTimeOut, string tokenSource)
        {
            //如果来源为 userservice.yamtou.com 则忽略           
            //获取token
            var tokenTable = UserTokenSpecifications.TokenCollectionName(tokenSource);
            var token = TokenRepo.Find(UserTokenSpecifications.MatchUserToken(userId), UserTokenSpecifications.TokenDatabaseName(), tokenTable).FirstOrDefault();
            //存在token 则直接返回
            if (token != null)
            {
                //判断UserToken 表是否存在token,不存在则创建 
                if (tokenTable != UserTokenSpecifications.TokenMainTable() && !TokenRepo.Exists(e => e.RawToken == token.RawToken))
                {
                    var uToken = new UserToken(userId, tokenTimeOut, tokenSource);
                    uToken.GenerateToken(token.RawToken);
                    TokenRepo.AddAsync(uToken);
                }
                return token;
            }
            else
            {
                var uToken = new UserToken(userId, tokenTimeOut, tokenSource);
                uToken.GenerateToken();
                //创建主表token        
                TokenRepo.AddAsync(uToken);
                //创建matouapp、app token
                if (tokenTable != UserTokenSpecifications.TokenMainTable())
                    TokenRepo.AddAsync(uToken, UserTokenSpecifications.TokenDatabaseName(), UserTokenSpecifications.TokenCollectionName(tokenSource), ex => YmatouLoggingService.Error("save token error {0}", ex.ToString()));
                return uToken;
            }
        }
        [Obsolete]
        private void SaveNewToken(UserTokenDto dto, UserToken uToken)
        {
            try
            {
                Task.Factory.StartNew(() =>
                {
                    //判断UserToken 表是否存在token
                    if (!TokenRepo.Exists(UserTokenSpecifications.MatchUserToken(dto.UserId)))
                        TokenRepo.Add(uToken);
                    //创建matouapp、app token
                    var tokenTable = UserTokenSpecifications.TokenCollectionName(dto.TokenSource);
                    if (tokenTable != UserTokenSpecifications.TokenMainTable())
                        TokenRepo.Add(uToken, UserTokenSpecifications.TokenDatabaseName(), tokenTable);
                });
            }
            catch (AggregateException ex)
            {
                foreach (var _ex in ex.InnerExceptions)
                    ex.Handler("{0}", _ex);
            }
            catch (Exception ex)
            {
                YmatouLoggingService.Error("保存token异常 {0}", ex);
                WriteLogAsync(uToken.TokenId, new SignAuthRequestDto { sClientType = dto.TokenSource, TokenSource = dto.TokenSource }, 0.0, uToken.UserId, "newtokenerror", ex.ToString());
            }
        }
        private ResponseData<TokenResponseDto> GetUserTokenByCache(string token)
        {
            var cacheUserId = TokenCache<string, int>.GetItem(token);
            if (cacheUserId > 0)
            {
                if (UserStatesAppService.EnableGetTokenCheckUserLocked())
                {
                    var userState = UserStatesAppService.CheckUserLocked(cacheUserId);
                    if (userState != UserStates.Normal)
                    {
                        TokenCache<string, int>.RemoveItem(token);
                        return ResponseData<TokenResponseDto>.CreateFail(new TokenResponseDto { Code = LoginResponseCode.UserLocked, UserData = new UserTokenData { } }, lastErrorMessage: "用户被锁定");
                    }
                }
                return ResponseData<TokenResponseDto>.CreateSuccess(new TokenResponseDto { Code = LoginResponseCode.OK, UserData = new UserTokenData { UserId = cacheUserId } }, "ok");
            }
            return ResponseData<TokenResponseDto>.CreateFail(new TokenResponseDto { Code = LoginResponseCode.TokenInvalid }, errorCode: "404", lastErrorMessage: "缓存中不存在用户token");
        }
        private ResponseData<SignAuthResponseDto2> GetUserTokenByCache(string originalVal, string token, out int userid)
        {
            var cacheUserId = TokenCache<string, int>.GetItem(originalVal);
            userid = cacheUserId;
            if (cacheUserId > 0)
            {
                if (UserStatesAppService.EnableSignTokenCheckUserLocked())
                {
                    var userState = UserStatesAppService.CheckUserLocked(cacheUserId);
                    if (userState != Domain.Shard.UserStates.Normal)
                    {
                        TokenCache<string, int>.RemoveItem(token);
                        return ResponseData<SignAuthResponseDto2>.CreateFail(new SignAuthResponseDto2 { Code = LoginResponseCode.UserLocked }, lastErrorMessage: "用户被锁定");
                    }
                }
                return ResponseData<SignAuthResponseDto2>.CreateSuccess(new SignAuthResponseDto2 { Code = LoginResponseCode.OK }, "ok");
            }
            return ResponseData<SignAuthResponseDto2>.CreateFail(new SignAuthResponseDto2 { Code = LoginResponseCode.TokenInvalid }, lastErrorMessage: "缓存中不存在token");
        }       
        #endregion
    }
}
